Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A first stab at a diagnostic surface temperature implementation #278

Merged
merged 54 commits into from
Dec 9, 2024

Conversation

simone-silvestri
Copy link
Collaborator

@simone-silvestri simone-silvestri commented Nov 26, 2024

This PR implements two types:

  • BulkTemperature
  • SkinTemperature

The first type is supposed to represent the default option we have know: the surface temperature is provided and equal to the temperature of "surface" model (either sea ice or ocean). This temperature is fixed and not iterated over.

The second type represents a diagnostic temperature that is diagnosed by a flux balance between the

  • top turbulent surfaces (dependent on the surface temperature and iterated by the solver)
  • top radiative fluxes (prescribed)
  • outgoing longwave (calculated from the surface temperature in the previous iteration)
  • internal fluxes: provided from the SkinTemperature, for the moment only a simple diffusive flux with prescribed diffusivity leading to an explicit expression for temperature, but they can become more complicated.

In this second case the temperature is solved for in the flux_balance_temperature(::SkinTemperature, T_ocean, net_external_flux) function

These types are provided to the similarity_theory as a new field named surface_temperature_type. The surface temperature is then stored in a T_surface field which is added to similarity_theory.fields

There is a bit of a pitfall in this implementation: it is difficult to implement temperature-dependent radiative properties, because radiative properties (that depend on i and j and the grid) are passed already computed to the solver.
I would be against passing i, j and the grid to the surface flux solver (it would entangle the solver with oceananigans so it would be difficult to swap it out with a SurfaceFluxes.jl solver), so we could rethink albedo and emissivity computation.

@simone-silvestri simone-silvestri marked this pull request as ready for review November 26, 2024 12:50
@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented Nov 26, 2024

A very crude first calculation in experiments/single_column_experiments/os_papa_surface_temperature.jl shows the difference between prescribed and diagnostic surface temperature. In the latter case, the internal fluxes are just a very simple diffusive flux with

$$\kappa \frac{T_s - T_o}{\Delta z}$$

where $\kappa = 10^{-2}$, $T_s$ is the incognita, $T_o$ is the temperature of the first ocean cell, and $\Delta z$ is half the upper cell spacing.
The solid lines are the results of DiagnosticSurfaceTemperature and the dashed ones are the respective variables calculated using PrescribedSurfaceTemperature

single_column_profiles.mp4

There is a large difference in the ocean surface temperature (about 1 degree K), but very little in the fluxes.

@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented Nov 26, 2024

However, the surface diffusivity predicted by CATKE in the above video is $O(1)$, much larger than the $\kappa = 10^{-2} m^2s^{-1}$ that I prescribed. If I use $\kappa = 0.5 m^2s^{-1}$ there is even less difference in the surface temperature and barely any in the heat fluxes.

single_column_profiles.mp4

I need to test this on the whole surface to see if there are maybe hot or cold spots that show more difference. As it stands, the net difference in heat flux is less than 1 $Wm^{-2}K^{-1}$. Difficult to assess the impact on a 10-day timescale.

Probably for sea ice where timescales are faster this might matter more.

@simone-silvestri simone-silvestri changed the title A first stab to diagnostic surface temperature A first stab at a diagnostic surface temperature implementation Nov 26, 2024
@simone-silvestri
Copy link
Collaborator Author

There is actually quite a large difference in net heat flux: This is the same computation as in the generate_surface_fluxes.jl but done also with a DiagnosticSurfaceTemperature with a diffusivity of 0.1.
Here I am showing the difference of net heat flux (Q_prescribed - Q_diagnostic) on the left and surface temperature (Ts_prescribed - Ts_diagnostic) on the right.
The surface temperature difference is upward of 2 degrees and the net heat flux has differences upward of 40 Wm2K-1 (which is quite large). In practice, a diagnostic temperature seems to have much more extreme fluxes, i.e heating regions (where solar radiation peaks) are heating much more and cooling regions are cooling much more. There might be a bug in the solver though, because some regions NaN.

Screenshot 2024-11-26 at 4 46 38 PM

@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented Nov 26, 2024

There were some gross bugs in the implementation (including the sign of the fluxes). Correcting the implementation the results look like this (same plot as before)
Screenshot 2024-11-26 at 5 19 18 PM

So in general a diagnostic temperature seems to be mitigating (slightly) the surface fluxes as it is intuitive it should do.
Surprisingly enough, it seems that the solver converges in average more quickly with a Diagnostic surface temperature
Screenshot 2024-11-26 at 5 25 39 PM

Copy link

codecov bot commented Dec 2, 2024

Codecov Report

Attention: Patch coverage is 0% with 67 lines in your changes missing coverage. Please review.

Project coverage is 0.00%. Comparing base (0fff4fa) to head (08be1bb).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...sRealmFluxes/similarity_theory_turbulent_fluxes.jl 0.00% 32 Missing ⚠️
...aIceModels/CrossRealmFluxes/surface_temperature.jl 0.00% 20 Missing ⚠️
...Models/CrossRealmFluxes/atmosphere_ocean_fluxes.jl 0.00% 9 Missing ⚠️
...rc/OceanSeaIceModels/CrossRealmFluxes/radiation.jl 0.00% 6 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main    #278   +/-   ##
=====================================
  Coverage   0.00%   0.00%           
=====================================
  Files         33      34    +1     
  Lines       1876    1910   +34     
=====================================
- Misses      1876    1910   +34     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@simone-silvestri
Copy link
Collaborator Author

Shall we merge?

@glwagner
Copy link
Member

glwagner commented Dec 5, 2024

There was a question about recomputing the saturation specific humidity. Was this changed and did it change the posted results?

@glwagner
Copy link
Member

glwagner commented Dec 5, 2024

It's a very nice result that it converges faster by the way.

@simone-silvestri
Copy link
Collaborator Author

There was a question about recomputing the saturation specific humidity. Was this changed and did it change the posted results?

Yep I changed it and indeed, the difference between the different solvers is much larger now in the order of 0.2 degrees instead of 0.02. I will post here another test showing the differences

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be ok to just have one Papa simulation, which uses the diagnostic surface temperautre (no need to keep the other one)

#
# Where the LHS is the internal diffusive flux inside the ocean (within the boundary layer of thickness δ)
# plus the (semi-implicit) outgoing longwave radiation and the RHS are the remaining atmospheric and radiative fluxes
# provided explicitly.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the comment, but which method was chosen?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, the fully explicit one. Should I remove the comment? Or maybe just update it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just add a remark about what the code does now; the linearization strategy is a potential optimization that isn't taken yet

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or you can remove it :-D

AtmosphericThermodynamics.Liquid())

𝒬ₛ = AtmosphericThermodynamics.PhaseEquil_pTq(ℂ, 𝒬₀.p, θ₀, q₀)
q₀ = AtmosphericThermodynamics.vapor_specific_humidity(ℂ, 𝒬ₛ)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't want to call this again, because the "seawater" part is important --- we account for the h2O mole fraction there. Also, we may be using a different saturation parameterization than AtmosphericThermodynamics (this is to comply with OMIP protocol)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm ok, if I remove that line the results are different (regression tests do not pass). Isn't ok that the seawater part called above?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird, not sure I understand what is going on then

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, were the regression tests also incorrect?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe I am confused. Does vapor_specific_humidity(ℂ, 𝒬ₛ) compute a new specific humidity or merely extract it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm confused sorry, I guess vapor_specific_humidity(ℂ, 𝒬ₛ) just extracts it. It says nothing about saturation there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I am weirded out that it changes the result. What is the difference between the two q0?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then probably yes, because in the main code we do:

q₁ = AtmosphericThermodynamics.vapor_specific_humidity(ℂ, 𝒬₁)
q₀ = AtmosphericThermodynamics.vapor_specific_humidity(ℂ, 𝒬₀)
Δq = q₁ - q₀

where 𝒬₀ holds the previously calculated q₀

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, on main what we do is compute q0, and then we assemble it into a thermodynamic / dynamic state. Then we pass the whole state to the solver. Within the solver, I guess it uses vapor_specific_humidity to extract q0 --- or at least, that's what I thought. This result suggests though that somehow q0 is changed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is changed in the vapor_specific_humidity function. It is kind of tough to follow the thread, but it looks like the vapor_specific_humidity builds a PhasePartition type to calculate the humidity vapor fraction associated with the thermodynamic state

@glwagner
Copy link
Member

glwagner commented Dec 5, 2024

There was a question about recomputing the saturation specific humidity. Was this changed and did it change the posted results?

Yep I changed it and indeed, the difference between the different solvers is much larger now in the order of 0.2 degrees instead of 0.02. I will post here another test showing the differences

Can you also show that the difference depends on the grid spacing at the top? It should right?

@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented Dec 9, 2024

Can you also show that the difference depends on the grid spacing at the top? It should right?

It does not for the moment because the boundary layer thickness δ is a parameter passed to the SkinTemperature type.
In practice, the first cell is assumed to be within the well-mixed region, so T represents the (mixed) cell average, and δ is the boundary layer at the top of the mixed region. These are the differences with the default parameters for SkinTemperature (κ = 0.01 and δ = 1.0) (BulkTemperature - SkinTemperature)

Screenshot 2024-12-09 at 1 02 16 PM

These parameters probably require calibration.

@glwagner
Copy link
Member

glwagner commented Dec 9, 2024

Can you also show that the difference depends on the grid spacing at the top? It should right?

It does not for the moment because the boundary layer thickness δ is a parameter passed to the SkinTemperature type. In practice, the first cell is assumed to be within the well-mixed region, so T represents the (mixed) cell average, and δ is the boundary layer at the top of the mixed region. These are the differences with the default parameters for SkinTemperature (κ = 0.01 and δ = 1.0) (BulkTemperature - SkinTemperature)

Hmm right, maybe it shouldn't depend on the grid spacing. That's helpful actually.

@glwagner glwagner merged commit b73c67c into main Dec 9, 2024
19 checks passed
@simone-silvestri simone-silvestri deleted the ss/new-fluxes branch December 9, 2024 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants